/**
 * @author Da Mu
 * @version v0.1.0
 * @mail 317782199@qq.com
 */
import {
    WebSocketGateway,
    WebSocketServer,
    SubscribeMessage,
    MessageBody,
    ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { TemplateApiGameRoom } from '../TemplateApiGameSocketRoom/TemplateApiGame.room';
import { TemplateApiRedisService } from '../TemplateApiPlugService/TemplateApiRedis.service';
import { forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger as WinstonLogger } from 'winston';


class OpenAirCronJob {
    static instance: any;
    static air(that: any) {
        that.gameRoom.publicAirStatusCronJob(that.server);
    };

    static building(that: any) {
        that.gameRoom.publicBuildingStatusCronJob(that.server);
    };

    static main(that: any) {
        if (!OpenAirCronJob.instance) {
            OpenAirCronJob.instance = new OpenAirCronJob();
            OpenAirCronJob.air(that);
        };
        return OpenAirCronJob.instance;
    };
};

class OpenBuildingCronJob {
    static instance: any;

    static building(that: any) {
        that.gameRoom.publicBuildingStatusCronJob(that.server);
    };

    static main(that: any) {
        if (!OpenBuildingCronJob.instance) {
            OpenBuildingCronJob.instance = new OpenBuildingCronJob();
            OpenBuildingCronJob.building(that);
        };
        return OpenBuildingCronJob.instance;
    };
};

@WebSocketGateway({
    path: '/api/v1/ws',
    cors: {
        origin: '*'
    },
})
@Injectable()
export class TemplateApiChatGateway {
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: WinstonLogger
    constructor(
        /**
         * @description Solving the problem of dependency injection and mutual cyclic dependencies
         */
        @Inject(forwardRef(() => TemplateApiGameRoom))
        private readonly gameRoom: TemplateApiGameRoom,
        private readonly redis: TemplateApiRedisService
    ) { };
    @WebSocketServer()
    server: Server;

    async handleConnection(@ConnectedSocket() client: Socket) {
        console.log(`Service socket.io connect: ${client.id}`);

        this.gameRoom.playersOnlineNumber = await this.redis.incr('players_online_number');
        if (this.gameRoom.playersOnlineNumber === 1) {
            await this.redis.expire('players_online_number', 86400);
        };

        /** check players Abnormal exit Disaster recovery processing */
        const players = await this.redis.getAllElements('DisasterRecoveryPlayer');
        if (players !== null) {
            players.map(async id => {
                this.server.emit('SomeoneLeftTheRoom', id);
                await this.redis.lrem('DisasterRecoveryPlayer', 0, id);
            });
        };

        OpenAirCronJob.main(this);
        OpenBuildingCronJob.main(this);
        this.logger.info({ event: 'socketConnection', clientId: `${client.id}` });
    };

    async handleDisconnect(@ConnectedSocket() client: Socket) {
        console.log(`Service socket.io disconnect: ${client.id}`);

        let count = await this.redis.decr('players_online_number');

        if (count < 0) {
            count = 0;
            await this.redis.set('players_online_number', 0);
        };

        this.gameRoom.playersOnlineNumber = count;
        this.logger.info({ event: 'socketDisconnect', clientId: `${client.id}` });
    };

    /**
     * 
     * @param client
     * @param data
     * @description IO_ROOM_JOIN_GAME_WORLD
     */
    @SubscribeMessage('IO_ROOM_JOIN_GAME_WORLD')
    io_room_join_game_world(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: string,
    ): void {
        if (data === 'JOIN') {
            this.gameRoom.client = client;
            this.gameRoom.server = this.server;
            this.gameRoom.publicJoinGameWorld();
        };
    };

    /**
     * 
     * @param client 
     * @param data 
     * @description IO_ROOM_PLAYERS_READY_START_GAME
     */
    @SubscribeMessage('IO_ROOM_PLAYERS_READY_START_GAME')
    io_room_players_ready_start_game(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: any
    ): void {
        let body: any;
        try {
            body = JSON.parse(data);
        } catch (error) {
            body = data;
        };

        this.gameRoom.client = client;
        this.gameRoom.server = this.server;
        this.gameRoom.publicPlaersReadyStartGame(body);
    };

    /**
     * 
     * @param client 
     * @param data 
     * @description IO_ROOM_PLAYERS_START_GAME
     */
    @SubscribeMessage('IO_ROOM_PLAYERS_START_GAME')
    io_room_players_start_game(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: any
    ): void {
        let body: any;
        try {
            body = JSON.parse(data);
        } catch (error) {
            body = data;
        };

        this.gameRoom.client = client;
        this.gameRoom.server = this.server;
        this.gameRoom.publicPlaersStartGame(body);
    };

    /**
     * 
     * @param client 
     * @param data 
     * @description IO_ROOM_PLAYERS_INFO_TIMER
     */
    @SubscribeMessage('IO_ROOM_PLAYERS_INFO_TIMER')
    io_room_player_start_info(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: any
    ): void {
        let body: any;
        try {
            body = JSON.parse(data);
        } catch (error) {
            body = data;
        };

        this.gameRoom.client = client;
        this.gameRoom.server = this.server;
        this.gameRoom.publicPlayersInfo(body);
    };

    /**
     * 
     * @param client 
     * @param data 
     * @description IO_ROOM_UI_STATUS_PLAYER_VALUE
     */
    @SubscribeMessage('IO_ROOM_UI_STATUS_PLAYER_VALUE')
    io_room_ui_status_player_value(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: any
    ): void {
        let body: any;
        try {
            body = JSON.parse(data);
        } catch (error) {
            body = data;
        };

        this.gameRoom.client = client;
        this.gameRoom.server = this.server;
        this.gameRoom.publicStatusPlayerValue(body);
    };

        /**
     * 
     * @param client 
     * @param data 
     * @description IO_ROOM_ENABLE3D_SWITCH_SCENE
     */
    @SubscribeMessage('IO_ROOM_ENABLE3D_SWITCH_SCENE')
    io_room_enable3d_switch_scene(
        @ConnectedSocket() client: Socket,
        @MessageBody() data: any
    ): void {
        let body: any;
        try {
            body = JSON.parse(data);
        } catch (error) {
            body = data;
        };

        this.gameRoom.client = client;
        this.gameRoom.server = this.server;
        this.gameRoom.publicEnable3dSwitchScene(body);
    };

};